﻿#include "FITKSEASubSysGeoProp.h"
#include "FITKSEASubSysBeam.h"
#include "FITKSEASubSysAbsShell.h"
#include "FITKSEASubSysCavity.h"
#include "FITKAcousticsDiscModel.h"
#include "FITKAcousticsSEAModel.h"
#include "FITKAcousticsDiscNodeList.h"
#include "FITKSEASubSysModelProp.h"
#include "FITKAbstractSEAFace.h"
#include "FITKSEASubSysShellCurved.h"
#include "FITKAcousticsTriangles.h"
#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Interface/FITKInterfaceModel/FITKElementTri.h"
#include "FITK_Component/FITK2DMeshTools/FITKAbstractRecogClosedSpace.h"
#include "FITK_Kernel/FITKCore/FITKVec3DAlg.h"
#include "FITK_Kernel/FITKCore/FITKTriangleAlg.h"
#include "FITK_Component/FITK2DMeshTools/FITKAbstractPlateCurvature.h"
#include <cmath>

namespace Acoustics
{
    FITKSEASubSysGeoProp::FITKSEASubSysGeoProp(FITKAbstractSEASubSys* subsys)
        : _subSys(subsys)
    {
    }

    bool FITKSEASubSysGeoProp::updateProp()
    {
        //错误判断
        if (_subSys == nullptr) return false;
        //子系统类型
        FITKAbstractSEASubSys::SEASubSysType t = _subSys->getSEASubSysType();
        switch (t)
        {
            //梁属性
        case Acoustics::FITKAbstractSEASubSys::SEABeam:
            this->calculateBeamProp();
            break;
            //板属性
        case Acoustics::FITKAbstractSEASubSys::SEAFlatPlate:
        case Acoustics::FITKAbstractSEASubSys::SEASinglyCurvedShell:
        case Acoustics::FITKAbstractSEASubSys::SEADoublyCurvedShell:
        case Acoustics::FITKAbstractSEASubSys::SEACylinderShell:
        {
            this->calculateShellProp();
            break;
        }
            //声腔属性
        case Acoustics::FITKAbstractSEASubSys::SEACavity:
        {
            this->calculateCavityProp();
            this->calculateShellDir();
            break;
        }
        case Acoustics::FITKAbstractSEASubSys::SEASemiIInfiniteFluid:
        default: return false;
        }
        return true;
    }

    double FITKSEASubSysGeoProp::getGeoProp(GeoPropType t)
    {
        switch (t)
        {
        case Acoustics::FITKSEASubSysGeoProp::Circumference: //周长
            return _geoprop[0];
        case Acoustics::FITKSEASubSysGeoProp::Area:          //面积
            return _geoprop[1];
        case Acoustics::FITKSEASubSysGeoProp::Volume:        //体积
            return _geoprop[2];
        case Acoustics::FITKSEASubSysGeoProp::Length:        //长
            return _geoprop[3];
        case Acoustics::FITKSEASubSysGeoProp::Width:         //宽
            return _geoprop[4];
        default: break;
        }
        return 0;
    }

    void FITKSEASubSysGeoProp::setGeoProp(GeoPropType t, double value)
    {
        switch (t)
        {
        case Acoustics::FITKSEASubSysGeoProp::Circumference: //周长
             _geoprop[0] = value;
             break;
        case Acoustics::FITKSEASubSysGeoProp::Area:          //面积
             _geoprop[1] = value;
             break;
        case Acoustics::FITKSEASubSysGeoProp::Volume:        //体积
             _geoprop[2] = value;
             break;
        case Acoustics::FITKSEASubSysGeoProp::Length:        //长
             _geoprop[3] = value;
             break;
        case Acoustics::FITKSEASubSysGeoProp::Width:         //宽
             _geoprop[4] = value;
             break;
        default: break;
        }
    }

    int FITKSEASubSysGeoProp::getShellDirOnCavity(const int shellid)
    {
        if (_shellDir.contains(shellid))
            return _shellDir.value(shellid);
        return 0;
    }

    QList<QPair<int, int>> FITKSEASubSysGeoProp::getBoundary()
    {
        QList<QPair<int, int>> nodeIDs;
        if (!_subSys) return nodeIDs;
        FITKAbstractSEASubSys::SEASubSysType type = _subSys->getSEASubSysType();
        if (type == FITKAbstractSEASubSys::SEASubSysType::SEACavity)
        {
            //声腔数据
            FITKSEASubSysCavity* cavity = dynamic_cast<FITKSEASubSysCavity*>(_subSys);
            if (!cavity)return nodeIDs;
            int count = cavity->getFaceCount();
            for (int i = 0; i < count; ++i)
            {
                FITKAbstractSEAFace* faceData = cavity->getFace(i);
                if (!faceData) continue;
                FITKSEASubSysModelProp* modelProp = faceData->getModelPorp();
                if (!modelProp) continue;
                nodeIDs.append(modelProp->getBoundary());
            }
        }
        else
        {
            //板数据
            FITKAbstractSEASubSysWithNodes* obj = dynamic_cast<FITKAbstractSEASubSysWithNodes*>(_subSys);
            if (!obj)return nodeIDs;
            FITKAbstractSEAFace* faceData = obj->getFaceObj();
            if (!faceData)return nodeIDs;
            FITKSEASubSysModelProp* modelProp = faceData->getModelPorp();
            if (!modelProp) return nodeIDs;
            nodeIDs = modelProp->getBoundary();
        }
        return nodeIDs;
    }

    void FITKSEASubSysGeoProp::calculateBeamProp()
    {
        //梁属性，长度
        FITKSEASubSysBeam* beam = dynamic_cast<FITKSEASubSysBeam*>(_subSys);
        if (!beam)return;
        //获取节点管理器 
        FITKAcousticsDiscModel *discModel = FITKAPP->getGlobalData()->getMeshData<FITKAcousticsDiscModel>();
        if (!discModel) return;
        FITKAcousticsDiscNodeList *discNodeList = discModel->getNodes();
        if (!discNodeList) return;
        //存储节点
        QList<Core::FITKNode*> nodeList;
        //获取所有节点
        QList<int> nodeIDsList = beam->getNodeIDs();
        for (int id : nodeIDsList)
        {
            Core::FITKNode *node = discNodeList->getNodeByID(id);
            if (!node)continue;
            nodeList.append(node);
        }
        if (nodeList.isEmpty()) return;
        double Circumference = 0.0;
        for (int i = 0; i < nodeList.size() - 2; ++i)
        {
            Core::FITKNode *node1 = nodeList.at(i), *node2 = nodeList.at(i + 1);
            if (!node1 || !node2)continue;
            Core::FITKPoint subData = Core::Subtract(*node1, *node2);
            Core::FITKVec3D vecData(subData);
            Circumference += vecData.getNorm();
        }
        _geoprop[0] = Circumference;
    }

    void FITKSEASubSysGeoProp::calculateShellProp()
    {
        FITKAbstractSEASubSysShell* shell = dynamic_cast<FITKAbstractSEASubSysShell*>(_subSys);
        if (shell == nullptr) return;
    //获取节点和网格数据
        QList<int> nodeSubSysList = shell->getTriangleNodeIDs();
        FITKAcousticsTriangles* mesh = shell->getTriangleMesh();
        if (nodeSubSysList.isEmpty() || !mesh)return;
    //获取节点管理器 
        FITKAcousticsDiscModel *discModel = FITKAPP->getGlobalData()->getMeshData<FITKAcousticsDiscModel>();
        if (!discModel) return;
        FITKAcousticsDiscNodeList *discNodeList = discModel->getNodes();
        if (!discNodeList) return;
    //存储点和单元
        QList<Core::FITKNode*> nodeList;
        QList<Interface::FITKElementTri3*> eleList;
    //存储计算结果
        double Area = 0, Circumference = 0;
    //获取节点数据
        for (int nID : nodeSubSysList)
        {
            Core::FITKNode *node = discNodeList->getNodeByID(nID);
            if (!node)continue;
            nodeList.append(node);
        }
    //获取单元数据
        int eleNum = mesh->getElementCount();
        for (int i = 0; i < eleNum; ++i)
        {
            Interface::FITKElementTri3* ele = dynamic_cast<Interface::FITKElementTri3*>(mesh->getElementAt(i));
            if (!ele) continue;
            eleList.append(ele);  

        //计算面积
            QList<Core::FITKNode*> s_nodeList;
            if (ele->getNodeCount() != 3)return;
            for (int i = 0; i < 3; i++)
            {
                Core::FITKNode *node = discNodeList->getNodeByID(ele->getNodeID(i));
                s_nodeList += node;
            }
            Area += Core::GetTriangleArea(*s_nodeList[0], *s_nodeList[1], *s_nodeList[2]);
        }
    //计算周长
        QList<QPair<int, int>> bound = this->getBoundary();
        for (QList<QPair<int, int>>::iterator iter = bound.begin(); iter < bound.end(); ++iter)
        {
            int nodeID1 = iter->first, nodeID2 = iter->second;
            Core::FITKNode *node1 = discNodeList->getNodeByID(nodeID1), *node2 = discNodeList->getNodeByID(nodeID2);
            if (!node1||!node2)continue;
            Core::FITKPoint subData = Core::Subtract(*node1, *node2);
            Core::FITKVec3D vecData(subData);
            Circumference += vecData.getNorm();
        }
    //计算结果
        _geoprop[0] = Circumference, _geoprop[1] = Area;
        //计算长和宽
        this->calcShellPropLengthWidth(nodeList);
    }

    void FITKSEASubSysGeoProp::calcShellPropLengthWidth(QList<Core::FITKNode*>& nodeList)
    {
        //计算长和宽-平板和单曲率板：LW=A, 2*(L+W)=C 双曲率：L为斜边长, W=A/L
        if (!_subSys || nodeList.isEmpty())return;
        FITKAbstractSEASubSys::SEASubSysType type = _subSys->getSEASubSysType();
        if (type == FITKAbstractSEASubSys::SEASubSysType::SEADoublyCurvedShell)
        {
            FITKSEASubSysDoublyCurvedShell* doublyCurvedShell = dynamic_cast<FITKSEASubSysDoublyCurvedShell*>(_subSys);
            if (!doublyCurvedShell)return;
            int nodeFringeCount = (nodeList.size() - 1) / doublyCurvedShell->getNumberOfRing();
            //获取最后一个点-顶点
            Core::FITKNode* nodeApex = nodeList.last();
            double length = 0;
            for (int i = 0; i < nodeFringeCount; ++i)
            {
                Core::FITKNode* node = nodeList.at(i);
                if (!node)continue;
                Core::FITKPoint subData = Core::Subtract(*node, *nodeApex);
                Core::FITKVec3D vecData(subData);
                double v = vecData.getNorm();
                if (v > length)
                    length = v;
            }
            _geoprop[3] = length;
            _geoprop[4] = _geoprop[1]/ length;
        }
        else if (type == FITKAbstractSEASubSys::SEASubSysType::SEASinglyCurvedShell || type == FITKAbstractSEASubSys::SEASubSysType::SEAFlatPlate)
        {
            //转化为一元二次方程 0=x2-(C/2)x+A,根据因式分解法求解x
            double a = 1.0, b = -(_geoprop[0]/2), c = _geoprop[1];
            double delta = b * b - 4 * a*c;
            //判断是否有解
            if (delta < 0)return;
            double x1 = (-b + sqrt(delta)) / 2 * a;
            double x2 = (-b - sqrt(delta)) / 2 * a;
            if (x1 < 0 || x2 < 0)return;
            //获取最值
            Core::FITKNode* node1 = nodeList.at(0);
            double xBestValue[2]{ node1->x(), node1->x() };
            double yBestValue[2]{ node1->y(), node1->y() };
            for (QList<Core::FITKNode*>::iterator iter = nodeList.begin(); iter != nodeList.end(); ++iter)
            {
                if (!*iter)continue;
                double x = (*iter)->x();
                double y = (*iter)->y();
                if (x > xBestValue[1])
                    xBestValue[1] = x;
                if (x < xBestValue[0])
                    xBestValue[0] = x;
                if (y > yBestValue[1])
                    yBestValue[1] = x;
                if (y < yBestValue[0])
                    yBestValue[0] = x;
            }
            //x轴的分量是长，y轴的分量是宽，对比差值来判断
            if (xBestValue[1] - xBestValue[0] > yBestValue[1] - yBestValue[0])
            {
                _geoprop[3] = x1;
                _geoprop[4] = x2;
            }
            else
            {
                _geoprop[4] = x1;
                _geoprop[3] = x2;
            }
        }
    }

    void FITKSEASubSysGeoProp::calculateCavityProp()
    {
        //获取声腔
        FITKSEASubSysCavity* cavity = dynamic_cast<FITKSEASubSysCavity*>(_subSys);
        if (cavity == nullptr) return;
        int faceCount = cavity->getFaceCount();
        if (faceCount == 0)return;
        //获取节点管理器 
        FITKAcousticsDiscModel *discModel = FITKAPP->getGlobalData()->getMeshData<FITKAcousticsDiscModel>();
        if (!discModel) return;
        FITKAcousticsDiscNodeList *discNodeList = discModel->getNodes();
        if (!discNodeList) return;
        //存储节点和单元
        QList<Core::FITKNode*> nodeList;
        QList<Interface::FITKElementTri3*> eleList;
        //获取所有节点
        QList<int> nodeIDsList = cavity->getTriangleNodeIDs();
        for (int id : nodeIDsList)
        {
            Core::FITKNode *node = discNodeList->getNodeByID(id);
            if (!node)continue;
            nodeList.append(node);
        }
        
        for (int i = 0; i < faceCount; ++i)
        {
            Acoustics::FITKAbstractSEAFace* faceData = cavity->getFace(i);
            if (!faceData)continue;
            //获取所有单元
            FITKAcousticsTriangles* mesh = faceData->getTriangleMesh();
            if (!mesh)continue;
            int eleNum = mesh->getElementCount();
            for (int i = 0; i < eleNum; ++i)
            {
                Interface::FITKElementTri3* ele = dynamic_cast<Interface::FITKElementTri3*>(mesh->getElementAt(i));
                if (!ele) continue;
                eleList.append(ele);
            }
        }
        if (nodeList.isEmpty() || eleList.isEmpty())return;
        //计算体积
        //Interface::FITKCalculateVolume calcVolume;
        //if (!calcVolume.calVolume(nodeList, eleList))
        //    return;
        //计算面积 和 周长
        double area = 0.0;
        double circumference = 0.0;
        int count = cavity->getShellCount();
        for (int i = 0; i < count; ++i)
        {
            FITKAbstractSEASubSys* subSysData = cavity->getShell(i);
            if (!subSysData) continue;
            FITKSEASubSysGeoProp* geoProp = subSysData->getGeoProp();
            if (!geoProp) continue;
            circumference += geoProp->getGeoProp(FITKSEASubSysGeoProp::GeoPropType::Circumference);
            area += geoProp->getGeoProp(FITKSEASubSysGeoProp::GeoPropType::Area);
        }

        // 计算结果
        _geoprop[0] = circumference;
        _geoprop[1] = area;
       /* _geoprop[2] = calcVolume.getVolume();*/
    }

    void FITKSEASubSysGeoProp::calculateShellDir()
    {
        FITKSEASubSysCavity* cavity = dynamic_cast<FITKSEASubSysCavity*>(_subSys);
        if (cavity == nullptr) return;
        int count = cavity->getShellCount();
        for (int i = 0; i < count; ++i)
        {
            int shellID = cavity->getShellID(i);
            int dir = this->getShellDir(shellID);
            _shellDir.insert(shellID, dir);
        }
    }

    int FITKSEASubSysGeoProp::getShellDir(int shellID)
    {
        FITKSEASubSysCavity* cavity = dynamic_cast<FITKSEASubSysCavity*>(_subSys);
        if (cavity == nullptr) return 0;
        // 判断声腔是否包含板，不包含则返回错误值0
        const int index = cavity->getShellIndex(shellID);
        FITKAbstractSEASubSysShell* shell = cavity->getShell(index);
        if (shell == nullptr) return 0;

        //全部节点坐标
        FITKAcousticsDiscModel *discModel = FITKAcousticsDiscModel::GetDiscModelFromFrame();
        if (!discModel) return 0;
        FITKAcousticsDiscNodeList *discNodeList = discModel->getNodes();
        if (!discNodeList) return 0;

        //板上的第一个三角形作为判断对象
        FITKAcousticsTriangles* tri = shell->getTriangleMesh();
        if (tri == nullptr) return 0;
        Interface::FITKAbstractElement* ele = tri->getElementAt(0);
        if (ele == nullptr) return 0;
        //节点数判断
        const int nNode = ele->getNodeCount();
        if (nNode < 3)  return 0;
        //三角形三个点
        const int nodeA = ele->getNodeID(0);
        const int nodeB = ele->getNodeID(1);
        const int nodeC = ele->getNodeID(2);

        //计算声腔重心
        QList<int> cavIDs = cavity->getNodeIDs();
        const int nPts = cavIDs.size();
        if (nPts < 3) return 0;
        double cen[3] = { 0 };
        for (int id : cavIDs)
        {
            Core::FITKNode* node = discNodeList->getNodeByID(id);
            if (node == nullptr) continue;
            cen[0] += node->x() / nPts;
            cen[1] += node->y() / nPts;
            cen[2] += node->z() / nPts;
        }
        Core::FITKNode center(cen);
        //计算体积正负
        Core::FITKVec3D OA = Core::Subtract(center, *(discNodeList->getNodeByID(nodeA)));
        Core::FITKVec3D OB = Core::Subtract(center, *(discNodeList->getNodeByID(nodeB)));
        Core::FITKVec3D OC = Core::Subtract(center, *(discNodeList->getNodeByID(nodeC)));

        Core::FITKVec3D c = Core::CrossProduct(OA, OB);
        return Core::DotProduct(c, OC) < 0 ? -1 : 1;
    }
}


